home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / Issue66 / SQLComp / DMSQLUtils.pas < prev   
Encoding:
Pascal/Delphi Source File  |  2000-10-11  |  8.1 KB  |  246 lines

  1. unit DMSQLUtils;
  2.  
  3. interface
  4.  
  5. const
  6.   NullValueStr = '<null>';
  7.  
  8. const
  9.   // SQL statement portions
  10.   sqlSelect = 'select';
  11.   sqlWhere = 'where';
  12.   sqlFrom = 'from';
  13.   sqlGroup = 'group';
  14.   sqlOrder = 'order';
  15.   sqlBy = 'by';
  16.   sqlGroupBy = sqlGroup + ' ' + sqlBy;
  17.   sqlOrderBy = sqlOrder + ' ' + sqlBy;
  18.   sqlHaving = 'having';
  19.   sqlPlan = 'plan';
  20.   sqlUnion = 'union';
  21.  
  22.   sqlValidTrailingChars = ' '#$D#$A;
  23.  
  24. // This function extracts the select portion of the spedified SQL string.
  25. // Valid separators are spaces or $D$A (as in TQuery.SQL.Text). The select
  26. // keyword is not part of the result string.
  27. function ExtractSelectClause(SQL: string): string;
  28.  
  29. // This function extracts the from portion of the spedified SQL string.
  30. // Valid separators are spaces or $D$A (as in TQuery.SQL.Text). The from
  31. // keyword is not part of the result string.
  32. function ExtractFromClause(SQL: string): string;
  33.  
  34. // This function extracts the where portion of the spedified SQL string.
  35. // Valid separators are spaces or $D$A (as in TQuery.SQL.Text). The where
  36. // keyword is not part of the result string.
  37. function ExtractWhereClause(SQL: string): string;
  38.  
  39. // This function extracts the having portion of the spedified SQL string.
  40. // Valid separators are spaces or $D$A (as in TQuery.SQL.Text). The having
  41. // keyword is not part of the result string.
  42. function ExtractHavingClause(SQL: string): string;
  43.  
  44. // This function extracts the order by portion of the spedified SQL string.
  45. // Valid separators are spaces or $D$A (as in TQuery.SQL.Text). The order by
  46. // keywords are not part of the result string.
  47. function ExtractOrderByClause(SQL: string): string;
  48.  
  49. // This function extracts the group by portion of the spedified SQL string.
  50. // Valid separators are spaces or $D$A (as in TQuery.SQL.Text). The group by
  51. // keywords are not part of the result string.
  52. function ExtractGroupByClause(SQL: string): string;
  53.  
  54. // This function extracts the plan portion of the spedified SQL string.
  55. // Valid separators are spaces or $D$A (as in TQuery.SQL.Text). The plan
  56. // keyword is not part of the result string.
  57. function ExtractPlanClause(SQL: string): string;
  58.  
  59. // Wrapper around System.Insert; inserts Src at position Index of
  60. // Dest, and returns the resulting string.
  61. function MergeStr(Src, Dest: string; Index: Integer): string;
  62.  
  63. // A case insensitive Pos function.
  64. function InsensitivePos(Substr, Str: string): Integer;
  65.  
  66. // Converts a TDateTime to a string which is valid for a SQL parser
  67. // (tested with InterBase).
  68. function FormatSQLDateTime(ADateTime: TDateTime): string;
  69.  
  70. // Converts a string to a Variant, taking care of the special cases Null
  71. // and Unassigned.
  72. function SQLStringToVariant(SQL: string): Variant;
  73.  
  74. implementation
  75.  
  76. uses
  77.   SysUtils;
  78.  
  79. // internal routines
  80.  
  81. // The PosEnh function works like Pos, except for the fact that it returns 0
  82. // is Substr is not part of Str (nothing new so far) *and* delimited by
  83. // characters included in LeadTrailChars or (if LeadNothing is True) nothing,
  84. // that is the end or the beginning of the string.
  85. // In addition, this function is case insensitive.
  86. // Example:
  87. //   i := PosEnh('WHERE', Query1.SQL.Text, ' '$D$A, False);
  88. //   In this case, i will be <> 0 only if the SQL statement contains the WHERE
  89. //   keyword at the beginning or end of a line and delimited on the other side
  90. //   by a space character. A WHERE keyword alone on a line would do as well.
  91. function PosEnh(Substr, Str, LeadTrailChars: string; LeadTrailNothing: Boolean): Integer;
  92. var
  93.   LeadingChar, TrailingChar: Char;
  94.   MyKeyword: string;
  95.   Found: Boolean;
  96. begin
  97.   Found := False;
  98.   Result := InsensitivePos(Substr, Str);
  99.   if Result <> 0 then begin
  100.     MyKeyword := Copy(Str, Result, Length(Str));
  101.  
  102.     if not Found then begin
  103.       // If the substring is a the beginning or end of the string,
  104.       // optionally exit.
  105.       if LeadTrailNothing and ((Result = 1) or (Length(MyKeyword) = Length(Substr))) then
  106.         Found := True;
  107.     end;
  108.  
  109.     if not Found then begin
  110.       // If the substring is delimited by characters included in LeadTrailChars
  111.       // we have found it.
  112.       if (LeadTrailChars <> '') then begin
  113.         LeadingChar := Copy(Str, Result - 1, 1)[1];
  114.         TrailingChar := MyKeyword[Length(Substr) + 1];
  115.         if (Pos(LeadingChar, LeadTrailChars) <> 0)
  116.            and (Pos(TrailingChar, LeadTrailChars) <> 0) then
  117.           Found := True;
  118.       end;
  119.     end;
  120.  
  121.     if not Found then
  122.       // Recurse passing only the part of the string after the Result index.
  123.       Result := PosEnh(Substr, Copy(Str, Result + Length(Substr),
  124.         Length(Str)), LeadTrailChars, LeadTrailNothing);
  125.  
  126.     if not Found and (Result >= Length(Str)) then
  127.       // If the end of the string is reached,
  128.       // we haven't found what we were searching for.
  129.       Result := 0;
  130.   end;
  131. end;
  132.  
  133. // Calls PosEnh for each string in Substrs, returning a value <> 0 the first
  134. // time that PosEnh does so.
  135. function MultiPosEnh(Substrs: array of string; Str, TrailingChars: string; TrailingNothing: Boolean): Integer;
  136. var
  137.   g: Integer;
  138. begin
  139.   Result := 0;
  140.   for g := Low(Substrs) to High(Substrs) do begin
  141.     Result := PosEnh(Substrs[g], Str, TrailingChars, TrailingNothing);
  142.     if Result <> 0 then
  143.       Break;
  144.   end;
  145. end;
  146.  
  147. // Implements the Extract*Clause functions.
  148. function ExtractSQLClause(SQL, Clause: string; OtherClauses: array of string): string;
  149. var
  150.   AfterFoundClause: string;
  151.   FoundClausePos, OtherClausesPos: Integer;
  152. begin
  153.   Result := '';
  154.   FoundClausePos := PosEnh(Clause, SQL, sqlValidTrailingChars, True);
  155.   if FoundClausePos > 0 then begin
  156.     AfterFoundClause := Copy(SQL, FoundClausePos + Length(Clause), Length(SQL) - FoundClausePos);
  157.     OtherClausesPos := MultiPosEnh(OtherClauses, AfterFoundClause, sqlValidTrailingChars, True);
  158.     if OtherClausesPos > 0 then
  159.       Result := Trim(Copy(AfterFoundClause, 1, OtherClausesPos - 1))
  160.     else
  161.       Result := Trim(AfterFoundClause);
  162.   end;
  163. end;
  164.  
  165. // public routines
  166.  
  167. function ExtractSelectClause(SQL: string): string;
  168. begin
  169.   Result := ExtractSQLClause(SQL, sqlSelect, [sqlFrom]);
  170. end;
  171.  
  172. function ExtractFromClause(SQL: string): string;
  173. begin
  174.   Result := ExtractSQLClause(SQL, sqlFrom, [sqlWhere, sqlGroup, sqlHaving, sqlUnion, sqlPlan, sqlOrder]);
  175. end;
  176.  
  177. function ExtractWhereClause(SQL: string): string;
  178. begin
  179.   Result := ExtractSQLClause(SQL, sqlWhere, [sqlGroup, sqlHaving, sqlUnion, sqlPlan, sqlOrder]);
  180. end;
  181.  
  182. function ExtractHavingClause(SQL: string): string;
  183. begin
  184.   Result := ExtractSQLClause(SQL, sqlHaving, [sqlUnion, sqlPlan, sqlOrder]);
  185. end;
  186.  
  187. function ExtractOrderByClause(SQL: string): string;
  188. begin
  189.   Result := ExtractSQLClause(SQL, sqlOrder, ['']);
  190.   if Result <> '' then
  191.     Result := ExtractSQLClause(Result, sqlBy, ['']);
  192. end;
  193.  
  194. function ExtractGroupByClause(SQL: string): string;
  195. begin
  196.   Result := ExtractSQLClause(SQL, sqlGroup, ['']);
  197.   if Result <> '' then
  198.     Result := ExtractSQLClause(Result, sqlBy, [sqlHaving, sqlUnion, sqlPlan, sqlOrder]);
  199. end;
  200.  
  201. function ExtractPlanClause(SQL: string): string;
  202. begin
  203.   Result := ExtractSQLClause(SQL, sqlPlan, ['']);
  204. end;
  205.  
  206. function MergeStr(Src, Dest: string; Index: Integer): string;
  207. var
  208.   LocalDest: string;
  209. begin
  210.   LocalDest := Dest;
  211.   Insert(Src, LocalDest, Index);
  212.   Result := LocalDest;
  213. end;
  214.  
  215. function InsensitivePos(Substr, Str: string): Integer;
  216. begin
  217.   Result := Pos(AnsiLowerCase(Substr), AnsiLowerCase(Str));
  218. end;
  219.  
  220. function FormatSQLDateTime(ADateTime: TDateTime): string;
  221. var
  222.   Year, Month, Day: Word;
  223. const
  224.   MonthNames: array[1..12] of string =
  225.     ('jan', 'feb', 'mar', 'apr', 'may', 'jun',
  226.      'jul', 'aug', 'sep', 'oct', 'nov', 'dec');
  227. begin
  228.   DecodeDate(ADateTime, Year, Month, Day);
  229.   Result := IntToStr(Day) + '-' + MonthNames[Month] + '-' + IntToStr(Year);
  230.   // If there is a time portion, include it in the final string.
  231.   if Frac(ADateTime) <> 0 then
  232.     Result := Result + ' ' + FormatDateTime('hh"."nn"."ss', ADateTime);
  233. end;
  234.  
  235. function SQLStringToVariant(SQL: string): Variant;
  236. begin
  237.   if SQL = NullValueStr then
  238.     Result := Null
  239.   else if SQL = '' then
  240.     Result := Unassigned
  241.   else
  242.     Result := SQL;
  243. end;
  244.  
  245. end.
  246.